package com.quanxiaoha.weblog.web.service.impl;

import com.quanxiaoha.weblog.common.domain.dos.EmailVerificationCodeDO;
import com.quanxiaoha.weblog.common.domain.dos.UserDO;
import com.quanxiaoha.weblog.common.domain.dos.UserLoginLogDO;
import com.quanxiaoha.weblog.common.domain.mapper.EmailVerificationCodeMapper;
import com.quanxiaoha.weblog.common.domain.mapper.UserLoginLogMapper;
import com.quanxiaoha.weblog.common.domain.mapper.UserMapper;
import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum;
import com.quanxiaoha.weblog.common.exception.BizException;
import com.quanxiaoha.weblog.common.mail.MailHelper;
import com.quanxiaoha.weblog.common.utils.Response;
import com.quanxiaoha.weblog.common.utils.UserContextHolder;
import com.quanxiaoha.weblog.jwt.utils.JwtTokenHelper;
import com.quanxiaoha.weblog.web.model.vo.user.EmailLoginReqVO;
import com.quanxiaoha.weblog.web.model.vo.user.SendEmailCodeReqVO;
import com.quanxiaoha.weblog.web.service.EmailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Random;

/**
 * @author: 犬小哈
 * @url: www.quanxiaoha.com
 * @date: 2025-01-28 15:00
 * @description: 邮箱服务实现类
 **/
@Service
@Slf4j
public class EmailServiceImpl implements EmailService {

    @Autowired
    private EmailVerificationCodeMapper emailVerificationCodeMapper;
    
    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private UserLoginLogMapper userLoginLogMapper;
    
    @Autowired
    private MailHelper mailHelper;
    
    @Autowired
    private JwtTokenHelper jwtTokenHelper;
    
    @Autowired
    private HttpServletRequest request;
    
    @Autowired
    private UserContextHolder userContextHolder;

    @Override
    public Response sendEmailCode(SendEmailCodeReqVO sendEmailCodeReqVO) {
        String email = sendEmailCodeReqVO.getEmail();
        Integer type = sendEmailCodeReqVO.getType();
        
        // 检查是否频繁发送
        EmailVerificationCodeDO latestCode = emailVerificationCodeMapper.selectLatestByEmailAndType(email, type);
        if (Objects.nonNull(latestCode)) {
            // 如果最近一次发送时间在1分钟内，则拒绝发送
            if (latestCode.getCreateTime().isAfter(LocalDateTime.now().minusMinutes(1))) {
                return Response.fail("发送过于频繁，请稍后再试");
            }
        }
        
        // 如果是绑定邮箱，检查邮箱是否已被使用
        if (type.equals(EmailVerificationCodeDO.Type.BIND_EMAIL.getCode())) {
            UserDO existUser = userMapper.findByEmail(email);
            if (Objects.nonNull(existUser)) {
                return Response.fail("该邮箱已被其他用户绑定");
            }
        }
        
        // 生成6位数字验证码
        String code = generateCode();
        
        // 保存验证码到数据库
        EmailVerificationCodeDO verificationCode = EmailVerificationCodeDO.builder()
                .email(email)
                .code(code)
                .type(type)
                .used(false)
                .expireTime(LocalDateTime.now().plusMinutes(10)) // 10分钟过期
                .createTime(LocalDateTime.now())
                .build();
        
        emailVerificationCodeMapper.insert(verificationCode);
        
        // 发送邮件
        try {
            String subject = getEmailSubject(type);
            String content = getEmailContent(code, type);
            boolean success = mailHelper.sendHtml(email, subject, content);
            
            if (success) {
                log.info("邮箱验证码发送成功，邮箱：{}，类型：{}", email, type);
                return Response.success("验证码发送成功，请查收邮件");
            } else {
                log.error("邮箱验证码发送失败，邮箱：{}，类型：{}", email, type);
                return Response.fail("验证码发送失败，请稍后重试");
            }
        } catch (Exception e) {
            log.error("邮箱验证码发送失败，邮箱：{}，类型：{}", email, type, e);
            return Response.fail("验证码发送失败，请稍后重试");
        }
    }

    @Override
    public Response emailLogin(EmailLoginReqVO emailLoginReqVO) {
        String email = emailLoginReqVO.getEmail();
        String code = emailLoginReqVO.getCode();
        
        // 验证验证码
        if (!verifyEmailCode(email, code, EmailVerificationCodeDO.Type.LOGIN.getCode())) {
            // 记录登录失败日志
            recordLoginLog(null, email, UserLoginLogDO.LoginType.EMAIL_CODE, false, "验证码错误或已过期");
            return Response.fail("验证码错误或已过期");
        }
        
        // 查找用户
        UserDO user = userMapper.findByEmail(email);
        if (Objects.isNull(user)) {
            recordLoginLog(null, email, UserLoginLogDO.LoginType.EMAIL_CODE, false, "用户不存在");
            return Response.fail("该邮箱未绑定用户账号");
        }
        
        if (user.getIsDeleted()) {
            recordLoginLog(user.getId(), user.getUsername(), UserLoginLogDO.LoginType.EMAIL_CODE, false, "用户已被删除");
            return Response.fail("用户账号已被禁用");
        }
        
        // 标记验证码为已使用
        EmailVerificationCodeDO verificationCode = emailVerificationCodeMapper.selectValidCode(email, code, EmailVerificationCodeDO.Type.LOGIN.getCode());
        if (Objects.nonNull(verificationCode)) {
            emailVerificationCodeMapper.markAsUsed(verificationCode.getId());
        }
        
        // 生成JWT Token
        String token = jwtTokenHelper.generateToken(user.getUsername());
        
        // 更新用户最后登录时间和登录次数
        userMapper.updateLoginInfo(user.getId(), LocalDateTime.now());
        
        // 记录登录成功日志
        recordLoginLog(user.getId(), user.getUsername(), UserLoginLogDO.LoginType.EMAIL_CODE, true, null);
        
        log.info("用户邮箱验证码登录成功，用户名：{}", user.getUsername());
        
        return Response.success(token);
    }

    @Override
    public boolean verifyEmailCode(String email, String code, Integer type) {
        EmailVerificationCodeDO verificationCode = emailVerificationCodeMapper.selectValidCode(email, code, type);
        return Objects.nonNull(verificationCode);
    }

    @Override
    public Response bindEmail(String email, String code) {
        // 获取当前登录用户
        UserDO currentUser = userContextHolder.getCurrentUser();
        if (Objects.isNull(currentUser)) {
            throw new BizException(ResponseCodeEnum.UNAUTHORIZED);
        }
        
        // 验证验证码
        if (!verifyEmailCode(email, code, EmailVerificationCodeDO.Type.BIND_EMAIL.getCode())) {
            return Response.fail("验证码错误或已过期");
        }
        
        // 检查邮箱是否已被其他用户绑定
        UserDO existUser = userMapper.findByEmail(email);
        if (Objects.nonNull(existUser) && !existUser.getId().equals(currentUser.getId())) {
            return Response.fail("该邮箱已被其他用户绑定");
        }
        
        // 更新用户邮箱
        UserDO updateUser = UserDO.builder()
                .id(currentUser.getId())
                .email(email)
                .emailVerified(true)
                .updateTime(LocalDateTime.now())
                .build();
        
        userMapper.updateById(updateUser);
        
        // 标记验证码为已使用
        EmailVerificationCodeDO verificationCode = emailVerificationCodeMapper.selectValidCode(email, code, EmailVerificationCodeDO.Type.BIND_EMAIL.getCode());
        if (Objects.nonNull(verificationCode)) {
            emailVerificationCodeMapper.markAsUsed(verificationCode.getId());
        }
        
        log.info("用户邮箱绑定成功，用户名：{}，邮箱：{}", currentUser.getUsername(), email);
        
        return Response.success("邮箱绑定成功");
    }

    /**
     * 生成验证码
     */
    private String generateCode() {
        Random random = new Random();
        return String.format("%06d", random.nextInt(1000000));
    }

    /**
     * 获取邮件主题
     */
    private String getEmailSubject(Integer type) {
        switch (type) {
            case 1:
                return "Weblog - 登录验证码";
            case 2:
                return "Weblog - 注册验证码";
            case 3:
                return "Weblog - 找回密码验证码";
            case 4:
                return "Weblog - 邮箱绑定验证码";
            default:
                return "Weblog - 验证码";
        }
    }

    /**
     * 获取邮件内容
     */
    private String getEmailContent(String code, Integer type) {
        String operation;
        switch (type) {
            case 1:
                operation = "登录";
                break;
            case 2:
                operation = "注册";
                break;
            case 3:
                operation = "找回密码";
                break;
            case 4:
                operation = "绑定邮箱";
                break;
            default:
                operation = "验证";
        }
        
        return String.format(
                "<html><body>" +
                "<h2>Weblog 验证码</h2>" +
                "<p>您好！</p>" +
                "<p>您正在进行<strong>%s</strong>操作，验证码为：</p>" +
                "<div style='background-color: #f0f0f0; padding: 20px; margin: 20px 0; text-align: center;'>" +
                "<span style='font-size: 24px; font-weight: bold; color: #409EFF;'>%s</span>" +
                "</div>" +
                "<p>验证码10分钟内有效，请尽快使用。</p>" +
                "<p>如非本人操作，请忽略此邮件。</p>" +
                "<br>" +
                "<p>——来自 Weblog 团队</p>" +
                "</body></html>",
                operation, code
        );
    }

    /**
     * 记录登录日志
     */
    private void recordLoginLog(Long userId, String username, UserLoginLogDO.LoginType loginType, boolean success, String failReason) {
        String ipAddress = getClientIpAddress(request);
        String userAgent = request.getHeader("User-Agent");
        
        UserLoginLogDO loginLog = UserLoginLogDO.builder()
                .userId(userId)
                .username(username)
                .loginType(loginType.getCode())
                .ipAddress(ipAddress)
                .userAgent(userAgent)
                .loginTime(LocalDateTime.now())
                .success(success)
                .failReason(failReason)
                .build();
        
        userLoginLogMapper.insert(loginLog);
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        String xRealIP = request.getHeader("X-Real-IP");
        String remoteAddr = request.getRemoteAddr();

        if (StringUtils.hasText(xForwardedFor) && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        if (StringUtils.hasText(xRealIP) && !"unknown".equalsIgnoreCase(xRealIP)) {
            return xRealIP;
        }
        
        return remoteAddr;
    }
}
